{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "eae90798fac43d38",
   "metadata": {},
   "source": [
    "# 手写Function Call大模型\n",
    "作者：IT周瑜\n",
    "\n",
    "微信ID：it_zhouyu"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "965c51781e36b009",
   "metadata": {},
   "source": "所谓Function Call大模型，就是大模型知道什么问题要调用工具以及传什么参数，什么问题不需要调用工具而是直接回答。"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "# !pip install langchain\n",
    "# !pip install langchain_deepseek"
   ],
   "id": "5e220e3f7af8760d",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-09-26T04:22:20.430167Z",
     "start_time": "2025-09-26T04:22:20.347833Z"
    }
   },
   "cell_type": "code",
   "source": [
    "import os\n",
    "from typing import Annotated\n",
    "\n",
    "from langchain_core.tools import tool\n",
    "from langchain_deepseek import ChatDeepSeek\n",
    "\n",
    "\n",
    "@tool\n",
    "def get_weather(city: Annotated[str, \"城市名称\"]) -> str:\n",
    "    \"\"\"获取指定城市的天气\"\"\"\n",
    "    if city == \"上海\":\n",
    "        return \"阴天\"\n",
    "    if city == \"北京\":\n",
    "        return \"多云\"\n",
    "    return \"下雨\"\n",
    "\n",
    "\n",
    "print(get_weather.name)\n",
    "print(get_weather.description)\n",
    "print(get_weather.args)"
   ],
   "id": "e1a0b152361cc2e7",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "get_weather\n",
      "获取指定城市的天气\n",
      "{'city': {'description': '城市名称', 'title': 'City', 'type': 'string'}}\n"
     ]
    }
   ],
   "execution_count": 18
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-09-26T04:24:05.264153Z",
     "start_time": "2025-09-26T04:24:01.340080Z"
    }
   },
   "cell_type": "code",
   "source": [
    "os.environ[\"DEEPSEEK_API_KEY\"] = \"sk-a6a78e54f458437d9d315bf50fd3f663\"\n",
    "\n",
    "model = ChatDeepSeek(model=\"deepseek-chat\")\n",
    "\n",
    "tools = [get_weather]\n",
    "model_with_tools = model.bind_tools(tools)\n",
    "\n",
    "query = \"上海什么天气\"\n",
    "response = model_with_tools.invoke(query)\n",
    "# response返回的并不是最终的输出，而是一个Tool调用结果\n",
    "print(response)"
   ],
   "id": "de5245ef6f4fd592",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "content='我来帮您查询上海的天气情况。' additional_kwargs={'tool_calls': [{'id': 'call_00_e851sSgrUX2ca4cuepMPi5vw', 'function': {'arguments': '{\"city\": \"\\\\u4e0a\\\\u6d77\"}', 'name': 'get_weather'}, 'type': 'function', 'index': 0}], 'refusal': None} response_metadata={'token_usage': {'completion_tokens': 30, 'prompt_tokens': 155, 'total_tokens': 185, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 128}, 'prompt_cache_hit_tokens': 128, 'prompt_cache_miss_tokens': 27}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_f253fc19d1_prod0820_fp8_kvcache', 'id': '1afebb68-ad34-4f6f-a38b-4a78f5348e39', 'service_tier': None, 'finish_reason': 'tool_calls', 'logprobs': None} id='run--a972e461-a79d-4b08-9488-d94e9c4c4372-0' tool_calls=[{'name': 'get_weather', 'args': {'city': '上海'}, 'id': 'call_00_e851sSgrUX2ca4cuepMPi5vw', 'type': 'tool_call'}] usage_metadata={'input_tokens': 155, 'output_tokens': 30, 'total_tokens': 185, 'input_token_details': {'cache_read': 128}, 'output_token_details': {}}\n"
     ]
    }
   ],
   "execution_count": 19
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-09-26T04:26:08.205884Z",
     "start_time": "2025-09-26T04:26:08.190693Z"
    }
   },
   "cell_type": "code",
   "source": [
    "tool_call = response.tool_calls[0]\n",
    "\n",
    "# 执行工具\n",
    "tools_map = {tool.name: tool for tool in tools}\n",
    "result = tools_map[tool_call[\"name\"]].invoke(tool_call[\"args\"])\n",
    "print(result)"
   ],
   "id": "f8f1ceaf31744f87",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "阴天\n"
     ]
    }
   ],
   "execution_count": 20
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "tools = [\n",
    "    {\n",
    "        \"name\": \"get_weather\",\n",
    "        \"description\": \"用于获取指定城市的天气情况\",\n",
    "        \"parameters\": {\n",
    "            \"type\": \"object\",\n",
    "            \"properties\": {\n",
    "                \"city\": {\n",
    "                    \"type\": \"string\",\n",
    "                    \"description\": \"城市名，例如：北京、上海\"\n",
    "                }\n",
    "            },\n",
    "            \"required\": [\"city\"]\n",
    "        }\n",
    "    },\n",
    "    {\n",
    "        \"name\": \"search_online\",\n",
    "        \"description\": \"用于进行互联网实时搜索\",\n",
    "        \"parameters\": {\n",
    "            \"type\": \"object\",\n",
    "            \"properties\": {\n",
    "                \"query\": {\n",
    "                    \"type\": \"string\",\n",
    "                    \"description\": \"需要搜索的关键词或问题\"\n",
    "                }\n",
    "            },\n",
    "            \"required\": [\"query\"]\n",
    "        }\n",
    "    }\n",
    "]"
   ],
   "id": "de50c6861f78d64e",
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "id": "613ca955598753ab",
   "metadata": {},
   "source": [
    "zhouyu_chat_template = \"\"\"<|zhouyu_start|>system\n",
    "{tools}<|zhouyu_end|>\n",
    "<|zhouyu_start|>user\n",
    "{question}<|zhouyu_end|>\n",
    "<|zhouyu_start|>assistant\n",
    "{answer}\n",
    "<|zhouyu_end|>\"\"\""
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "id": "c80e7165d165de0b",
   "metadata": {},
   "source": [
    "data = [\n",
    "    {\n",
    "        \"tools\": tools,\n",
    "        \"question\": \"我想知道上海什么天气？\",\n",
    "        \"answer\": \"\"\"<tool_code>{\"name\": \"get_weather\",\"arguments\": {\"city\": \"上海\"}}</tool_code>\"\"\"\n",
    "    },\n",
    "    {\n",
    "        \"tools\": tools,\n",
    "        \"question\": \"什么是Transformer\",\n",
    "        \"answer\": \"\"\"<tool_code>{\"name\": \"search_online\",\"arguments\": {\"query\": \"什么是Transformer\"}}</tool_code>\"\"\"\n",
    "    },\n",
    "    {\n",
    "        \"tools\": tools,\n",
    "        \"question\": \"你今天心情怎么样？\",\n",
    "        \"answer\": \"心情很美丽\"\n",
    "    },\n",
    "    {\n",
    "        \"tools\": tools,\n",
    "        \"question\": \"帮我给it_zhouyu@qq.com发一封邮件，告诉他模型训练好了\",\n",
    "        \"answer\": \"对不起，我没有发送邮件的功能，无法完成您的请求。\"\n",
    "    }\n",
    "]"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "id": "d5230c4ecd75319f",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-09-26T04:36:22.830766Z",
     "start_time": "2025-09-26T04:36:22.816462Z"
    }
   },
   "source": [
    "test = zhouyu_chat_template.format(tools=data[0][\"tools\"], question=data[0][\"question\"], answer=data[0][\"answer\"])\n",
    "print(test)"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<|zhouyu_start|>system\n",
      "[{'name': 'get_weather', 'description': '用于获取指定城市的天气情况', 'parameters': {'type': 'object', 'properties': {'city': {'type': 'string', 'description': '城市名，例如：北京、上海'}}, 'required': ['city']}}, {'name': 'search_online', 'description': '用于进行互联网实时搜索', 'parameters': {'type': 'object', 'properties': {'query': {'type': 'string', 'description': '需要搜索的关键词或问题'}}, 'required': ['query']}}]<|zhouyu_end|>\n",
      "<|zhouyu_start|>user\n",
      "我想知道上海什么天气？<|zhouyu_end|>\n",
      "<|zhouyu_start|>assistant\n",
      "<tool_code>{\"name\": \"get_weather\",\"arguments\": {\"city\": \"上海\"}}</tool_code>\n",
      "<|zhouyu_end|>\n"
     ]
    }
   ],
   "execution_count": 21
  },
  {
   "cell_type": "code",
   "id": "11a007aab6708dc2",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-09-26T04:37:50.919480Z",
     "start_time": "2025-09-26T04:37:46.914007Z"
    }
   },
   "source": [
    "from modelscope import AutoTokenizer, AutoModelForCausalLM\n",
    "\n",
    "model_name = \"openai-community/gpt2\"\n",
    "model = AutoModelForCausalLM.from_pretrained(model_name)\n",
    "tokenizer = AutoTokenizer.from_pretrained(model_name)\n",
    "\n",
    "tokenizer.add_special_tokens({'bos_token': '<|zhouyu_start|>'})\n",
    "tokenizer.add_special_tokens({'eos_token': '<|zhouyu_end|>'})\n",
    "tokenizer.add_special_tokens({'pad_token': '<|endoftext|>'})\n",
    "\n",
    "model.resize_token_embeddings(len(tokenizer))"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Downloading Model from https://www.modelscope.cn to directory: /Users/dadudu/.cache/modelscope/hub/models/openai-community/gpt2\n",
      "Downloading Model from https://www.modelscope.cn to directory: /Users/dadudu/.cache/modelscope/hub/models/openai-community/gpt2\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "Embedding(50259, 768)"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 23
  },
  {
   "cell_type": "code",
   "id": "880f71b43ce0ba78",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-09-26T04:38:15.465916Z",
     "start_time": "2025-09-26T04:38:15.450551Z"
    }
   },
   "source": [
    "from torch.utils.data import Dataset\n",
    "\n",
    "\n",
    "# 微信id：it_zhouyu\n",
    "class ZhouyuDataset(Dataset):\n",
    "    def __init__(self, data, max_length=512):\n",
    "        self.encodings = []\n",
    "        for qa in data:\n",
    "            text = zhouyu_chat_template.format(tools=qa[\"tools\"], question=qa[\"question\"], answer=qa[\"answer\"])\n",
    "            encoded = tokenizer(\n",
    "                text,\n",
    "                max_length=max_length,\n",
    "                padding='max_length',\n",
    "                truncation=True,\n",
    "                return_tensors='pt'\n",
    "            )\n",
    "            input_ids = encoded['input_ids'].squeeze()\n",
    "            self.encodings.append(input_ids)\n",
    "\n",
    "    def __len__(self):\n",
    "        return len(self.encodings)\n",
    "\n",
    "    def __getitem__(self, idx):\n",
    "        return self.encodings[idx]\n",
    "\n",
    "\n",
    "dataset = ZhouyuDataset(data)"
   ],
   "outputs": [],
   "execution_count": 26
  },
  {
   "cell_type": "code",
   "id": "2e47fde54b6e976e",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-09-26T04:38:16.599122Z",
     "start_time": "2025-09-26T04:38:16.592431Z"
    }
   },
   "source": [
    "dataset[0]"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([50257, 10057,   198,    58,    90,     6,  3672, 10354,   705,  1136,\n",
       "           62, 23563,  3256,   705, 11213, 10354,   705, 18796,   101, 12859,\n",
       "          236,   164,   236,   115, 20998,   244,   162,   234,   229, 22522,\n",
       "          248,   161,   253,   236, 30585,   224, 21410, 25465, 36365,   242,\n",
       "        46349,   227, 37863,   113,  3256,   705, 17143,  7307, 10354,  1391,\n",
       "            6,  4906, 10354,   705, 15252,  3256,   705, 48310, 10354,  1391,\n",
       "            6, 19205, 10354,  1391,     6,  4906, 10354,   705,  8841,  3256,\n",
       "          705, 11213, 10354,   705,   161,   253,   236, 30585,   224, 28938,\n",
       "          235,   171,   120,   234,   160,   122,   233, 36685,   224,   171,\n",
       "          120,   248, 44293,   245, 12859,   105, 23513, 41468, 38184,   115,\n",
       "            6,    92,  5512,   705, 35827, 10354, 37250, 19205, 20520,    92,\n",
       "         5512,  1391,     6,  3672, 10354,   705, 12947,    62, 25119,  3256,\n",
       "          705, 11213, 10354,   705, 18796,   101, 12859,   236, 32573,   249,\n",
       "        26193,   234, 12859,   240,   164,   223,   242,   163,   121,   239,\n",
       "        22522,   252, 33768, 35050,   238,   250,   163,   112,    95,  3256,\n",
       "          705, 17143,  7307, 10354,  1391,     6,  4906, 10354,   705, 15252,\n",
       "         3256,   705, 48310, 10354,  1391,     6, 22766, 10354,  1391,     6,\n",
       "         4906, 10354,   705,  8841,  3256,   705, 11213, 10354,   705,   165,\n",
       "          250,   222, 17358,   223,   162,   238,   250,   163,   112,    95,\n",
       "        21410, 17739,   111,   165,   242,   106, 46237,   235, 22755,   244,\n",
       "        29785,   106,   165,    95,   246,     6,    92,  5512,   705, 35827,\n",
       "        10354, 37250, 22766, 20520, 11709,    60, 50258,   198, 50257,  7220,\n",
       "          198, 22755,   239, 46349,   111,   163,   253,    98, 34402,   241,\n",
       "        41468, 38184,   115, 20015,   222, 20046,   230, 25465, 36365,   242,\n",
       "          171,   120,   253, 50258,   198, 50257,   562, 10167,   198,    27,\n",
       "        25981,    62,  8189,    29,  4895,  3672,  1298,   366,  1136,    62,\n",
       "        23563,  2430,   853,  2886,  1298, 19779, 19205,  1298,   366, 41468,\n",
       "        38184,   115,     1, 11709,  3556, 25981,    62,  8189,    29,   198,\n",
       "        50258, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256,\n",
       "        50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256,\n",
       "        50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256,\n",
       "        50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256,\n",
       "        50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256,\n",
       "        50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256,\n",
       "        50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256,\n",
       "        50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256,\n",
       "        50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256,\n",
       "        50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256,\n",
       "        50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256,\n",
       "        50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256,\n",
       "        50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256,\n",
       "        50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256,\n",
       "        50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256,\n",
       "        50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256,\n",
       "        50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256,\n",
       "        50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256,\n",
       "        50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256,\n",
       "        50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256,\n",
       "        50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256,\n",
       "        50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256,\n",
       "        50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256, 50256,\n",
       "        50256, 50256])"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 27
  },
  {
   "cell_type": "code",
   "id": "df53a9dcaa1535fe",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-09-26T04:38:48.699894Z",
     "start_time": "2025-09-26T04:38:48.684603Z"
    }
   },
   "source": [
    "test = tokenizer.decode(dataset[0])\n",
    "print(test)"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<|zhouyu_start|>system\n",
      "[{'name': 'get_weather', 'description': '用于获取指定城市的天气情况', 'parameters': {'type': 'object', 'properties': {'city': {'type': 'string', 'description': '城市名，例如：北京、上海'}}, 'required': ['city']}}, {'name': 'search_online', 'description': '用于进行互联网实时搜索', 'parameters': {'type': 'object', 'properties': {'query': {'type': 'string', 'description': '需要搜索的关键词或问题'}}, 'required': ['query']}}]<|zhouyu_end|>\n",
      "<|zhouyu_start|>user\n",
      "我想知道上海什么天气？<|zhouyu_end|>\n",
      "<|zhouyu_start|>assistant\n",
      "<tool_code>{\"name\": \"get_weather\",\"arguments\": {\"city\": \"上海\"}}</tool_code>\n",
      "<|zhouyu_end|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|>\n"
     ]
    }
   ],
   "execution_count": 29
  },
  {
   "cell_type": "code",
   "id": "b186d07e13acb6f4",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-09-26T04:38:54.253643Z",
     "start_time": "2025-09-26T04:38:54.248971Z"
    }
   },
   "source": [
    "from transformers import DataCollatorForLanguageModeling\n",
    "\n",
    "# 创建数据收集器\n",
    "data_collator = DataCollatorForLanguageModeling(\n",
    "    tokenizer=tokenizer,\n",
    "    mlm=False  # 使用CLM（因果语言模型）\n",
    ")"
   ],
   "outputs": [],
   "execution_count": 30
  },
  {
   "cell_type": "code",
   "id": "a79834f145c5b86f",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-09-26T04:41:24.680634Z",
     "start_time": "2025-09-26T04:39:04.528469Z"
    }
   },
   "source": [
    "from transformers import Trainer, TrainingArguments\n",
    "\n",
    "# 训练配置\n",
    "training_args = TrainingArguments(\n",
    "    output_dir=\"./zhouyu_functioncall_model\",\n",
    "    per_device_train_batch_size=1,\n",
    "    num_train_epochs=100,\n",
    "    # eval_strategy=\"epoch\",\n",
    "    # save_strategy=\"epoch\",\n",
    "    logging_steps=10\n",
    ")\n",
    "\n",
    "# 创建Trainer\n",
    "trainer = Trainer(\n",
    "    model=model,\n",
    "    args=training_args,\n",
    "    train_dataset=dataset,\n",
    "    # eval_dataset=tokenized_datasets,\n",
    "    data_collator=data_collator\n",
    ")\n",
    "\n",
    "# 开始训练\n",
    "trainer.train()\n",
    "trainer.save_model(\"./zhouyu_functioncall_model/model\")"
   ],
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/dadudu/miniconda3/envs/mini-gpt/lib/python3.10/site-packages/torch/utils/data/dataloader.py:683: UserWarning: 'pin_memory' argument is set as true but not supported on MPS now, then device pinned memory won't be used.\n",
      "  warnings.warn(warn_msg)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ],
      "text/html": [
       "\n",
       "    <div>\n",
       "      \n",
       "      <progress value='400' max='400' style='width:300px; height:20px; vertical-align: middle;'></progress>\n",
       "      [400/400 02:09, Epoch 100/100]\n",
       "    </div>\n",
       "    <table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       " <tr style=\"text-align: left;\">\n",
       "      <th>Step</th>\n",
       "      <th>Training Loss</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <td>10</td>\n",
       "      <td>2.544300</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>20</td>\n",
       "      <td>1.316700</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>30</td>\n",
       "      <td>0.750200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>40</td>\n",
       "      <td>0.512500</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>50</td>\n",
       "      <td>0.406100</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>60</td>\n",
       "      <td>0.300200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>70</td>\n",
       "      <td>0.238600</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>80</td>\n",
       "      <td>0.213100</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>90</td>\n",
       "      <td>0.174500</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>100</td>\n",
       "      <td>0.153100</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>110</td>\n",
       "      <td>0.103700</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>120</td>\n",
       "      <td>0.086300</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>130</td>\n",
       "      <td>0.083100</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>140</td>\n",
       "      <td>0.066700</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>150</td>\n",
       "      <td>0.050900</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>160</td>\n",
       "      <td>0.046000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>170</td>\n",
       "      <td>0.071000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>180</td>\n",
       "      <td>0.045500</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>190</td>\n",
       "      <td>0.037500</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>200</td>\n",
       "      <td>0.036000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>210</td>\n",
       "      <td>0.033800</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>220</td>\n",
       "      <td>0.040400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>230</td>\n",
       "      <td>0.032200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>240</td>\n",
       "      <td>0.039200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>250</td>\n",
       "      <td>0.027100</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>260</td>\n",
       "      <td>0.028100</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>270</td>\n",
       "      <td>0.028000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>280</td>\n",
       "      <td>0.033700</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>290</td>\n",
       "      <td>0.027200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>300</td>\n",
       "      <td>0.026700</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>310</td>\n",
       "      <td>0.030900</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>320</td>\n",
       "      <td>0.023200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>330</td>\n",
       "      <td>0.033000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>340</td>\n",
       "      <td>0.026600</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>350</td>\n",
       "      <td>0.023200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>360</td>\n",
       "      <td>0.025100</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>370</td>\n",
       "      <td>0.020700</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>380</td>\n",
       "      <td>0.022900</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>390</td>\n",
       "      <td>0.024000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>400</td>\n",
       "      <td>0.026600</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table><p>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "execution_count": 31
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-09-26T04:44:01.697609Z",
     "start_time": "2025-09-26T04:44:01.687402Z"
    }
   },
   "cell_type": "code",
   "source": [
    "def get_weather(city: str) -> str:\n",
    "    \"\"\"用来获取天气\"\"\"\n",
    "    return f\"{city}天气是晴天\"\n",
    "\n",
    "chat_tools = [\n",
    "    {\n",
    "        \"name\": \"get_weather\",\n",
    "        \"description\": \"用来获取天气\",\n",
    "        \"parameters\": {\n",
    "            \"type\": \"object\",\n",
    "            \"properties\": {\n",
    "                \"city\": {\n",
    "                    \"type\": \"string\",\n",
    "                    \"description\": \"城市名，例如：北京、上海\"\n",
    "                }\n",
    "            },\n",
    "            \"required\": [\"city\"]\n",
    "        }\n",
    "    }\n",
    "]"
   ],
   "id": "2f434efd018d0613",
   "outputs": [],
   "execution_count": 32
  },
  {
   "cell_type": "code",
   "id": "5c758140081e417c",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-09-26T04:45:13.377712Z",
     "start_time": "2025-09-26T04:45:10.611604Z"
    }
   },
   "source": [
    "import torch\n",
    "\n",
    "chat_template = \"\"\"<|zhouyu_start|>system\n",
    "{tools}<|zhouyu_end|>\n",
    "<|zhouyu_start|>user\n",
    "{question}<|zhouyu_end|>\n",
    "<|zhouyu_start|>assistant\n",
    "\"\"\"\n",
    "\n",
    "prompt = \"我想知道上海什么天气？\"\n",
    "# prompt = \"你今天心情怎么样？\"\n",
    "\n",
    "text = chat_template.format(tools=chat_tools, question=prompt)\n",
    "\n",
    "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"mps\")\n",
    "model_inputs = tokenizer([text], return_tensors=\"pt\")\n",
    "model_inputs = model_inputs.to(device)\n",
    "generated_ids = model.generate(**model_inputs, max_new_tokens=200, pad_token_id=tokenizer.pad_token_id, eos_token_id=tokenizer.eos_token_id)\n",
    "content = tokenizer.decode(generated_ids[0])\n",
    "print(content)"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<|zhouyu_start|>system\n",
      "[{'name': 'get_weather', 'description': '用来获取天气', 'parameters': {'type': 'object', 'properties': {'city': {'type': 'string', 'description': '城市名，例如：北京、上海'}}, 'required': ['city']}}]<|zhouyu_end|>\n",
      "<|zhouyu_start|>user\n",
      "我想知道上海什么天气？<|zhouyu_end|>\n",
      "<|zhouyu_start|>assistant\n",
      "<tool_code>{\"name\": \"get_weather\",\"arguments\": {\"city\": \"上海\"}}</tool_code>\n",
      "<|zhouyu_end|>\n"
     ]
    }
   ],
   "execution_count": 36
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.18"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
